Breve introduzione
Il mercato musicale e discografico, nella nostra epoca, appare dominato dal digital delivery e dallo streaming. Ormai, oltre alle vendite dei CD e dei vinili, recentemente ritornati in auge, contano moltissimo (e forse quasi più del resto) gli ascolti e i follower sulle piattaforme social.
In ambito musicale ne esistono davvero molte: Youtube Music, Soundcloud, Tidal, Apple Music… ma la più popolare e nota al grande pubblico resta sicuramente Spotify, forte dei suoi 356 milioni di utenti attivi (di cui 158 premium), e dei suoi oltre 40.000 brani caricati al giorno.
L’obiettivo di questo progetto è quello di rispondere a delle semplici domande che un musicista, in procinto di scrivere e pubblicare dei brani su Spotify, potrebbe porsi: quali sono i trend del momento? Come devo scrivere e ideare i miei brani per cercare di avere successo? Cosa devo fare per avere tanti ascolti, ma soprattutto cosa non devo fare?
Elenco dei contenuti
Composizione del dataset e definizione delle features
Analisi sulle features
- Numero di brani in funzione dell’anno
- Durata media dei brani, suddivisi per anno di pubblicazione
- Distribuzione di frequenza dei valori assunti dalle features
- Correlazione tra le features
- Analisi musicali: chiavi e modi tonali dominanti
- Analisi dei brani più popolari
- Analisi dei generi
- Maggior popolarità
- Chiavi e modi dominanti nei generi
- Analisi dei trend musicali nel tempo
- Popolarità dei generi nel tempo
- Andamento delle features nel tempo
- Potenziali sviluppi nell’immediato futuro
- Andamento della durata
- Andamento dei generi
- Andamento delle features
- Sentiment Analysis
- Analisi dei testi dei brani più in voga
- Conclusioni
Prima di cominciare, importiamo le principali librerie necessarie, nonché il file csv.
library(tidyverse)
library(dplyr)
library(tidyr)
library(ggplot2)
spotify_db <- read.csv("./tracks.csv")Composizione del dataset e definizione delle features
Il dataset si compone di 2 documenti in formato .csv, contenenti rispettivamente
- L’elenco di brani presenti su Spotify, con relative features
- L’elenco dei generi, con il relativo valore medio delle features
Le features di ogni brano sono:
- Artists
L’elenco degli artisti della canzone.
- Danceability
La ballabilità descrive quanto un brano sia adatto al ballo basandosi su una combinazione di elementi musicali tra cui il tempo, la stabilità del ritmo, la forza del beat e la regolarità generale. Un valore di 0.0 è meno ballabile e 1.0 è più ballabile.
- Duration_ms
La durata della traccia in millisecondi.
- Energy
L’energia è una misura da 0.0 a 1.0 e rappresenta una misura percettiva di intensità e attività. Tipicamente, le tracce energetiche sono veloci, forti e rumorose. Per esempio, il death metal ha un’alta energia, mentre un preludio di Bach ottiene un punteggio basso. Le caratteristiche percettive che contribuiscono a questo attributo includono la gamma dinamica, l’intensità percepita, il timbro, la frequenza di inizio e l’entropia generale.
- Explicit
Indica se il brano include o meno del testo esplicito.
- Instrumentalness
Indica se un brano contiene voci. I suoni “Ooh” e “aah” sono trattati come strumentali in questo contesto. Le tracce rap ad esempio sono chiaramente “vocali”. Più il valore di instrumentalness è vicino a 1.0, maggiore è la probabilità che la traccia non contenga contenuti vocali. I valori superiori a 0,5 sono intesi come tracce strumentali, ma la fiducia è più alta quando il valore si avvicina a 1,0.
- Key
La chiave in cui si trova la traccia. I numeri interi mappano le altezze usando la notazione Standard Pitch Class. Per esempio 0 = C, 1 = C♯/D♭, 2 = D, e così via.
- Liveness
Rileva la presenza di un pubblico nella registrazione. Valori più alti di liveness rappresentano una maggiore probabilità che il brano sia stato eseguito dal vivo. Un valore superiore a 0,8 fornisce una forte probabilità che il brano sia dal vivo
- Loudness
Il volume complessivo di una traccia in decibel (dB). I valori di loudness sono una media di tutta la traccia e sono utili per confrontare il loudness relativo delle tracce. La loudness è la qualità di un suono che è il principale correlato psicologico della forza fisica (ampiezza). I valori tipici sono compresi tra -60 e 0 db.
- Mode
Il modo indica la modalità (maggiore o minore) di un brano, il tipo di scala da cui deriva il suo contenuto melodico. Maggiore è rappresentato da 1 e minore da 0.
- Name
Nome del brano.
- Popularity
La popolarità di un brano è un valore compreso tra 0 e 100, con 100 che è il più popolare. La popolarità è calcolata da un algoritmo e si basa, nella maggior parte dei casi, sul numero totale di riproduzioni che il brano ha avuto e su quanto sono recenti quelle riproduzioni. In generale, le canzoni che vengono suonate molto adesso avranno una popolarità più alta di quelle che sono state suonate molto in passato.
- Release_date
La data di uscita dell’album, per esempio “1981-12-15”. A seconda della precisione, potrebbe essere mostrato come “1981” o “1981-12”.
- Speechiness
Rileva la presenza di parole parlate in una traccia. Più la registrazione è esclusivamente parlata (ad esempio talk show, audiolibro, poesia), più il valore dell’attributo è vicino a 1.0. Valori superiori a 0,66 descrivono tracce che sono probabilmente fatte interamente di parole parlate. I valori tra 0,33 e 0,66 descrivono tracce che possono contenere sia musica che parlato, sia in sezioni che stratificate, inclusi casi come la musica rap. I valori inferiori a 0,33 rappresentano molto probabilmente musica e altre tracce non simili al parlato.
- Tempo
Il tempo complessivo stimato di una traccia in battiti al minuto (BPM). Nella terminologia musicale, il tempo è la velocità o il ritmo di un dato brano e deriva direttamente dalla durata media delle battute.
- Valence
Una misura da 0.0 a 1.0 che descrive la positività musicale trasmessa da un brano. I brani con alta valenza suonano più positivi (ad esempio, felice, allegro, euforico), mentre i brani con bassa valenza suonano più negativi (ad esempio, triste, depresso, arrabbiato).
- Year
Anno di uscita (estratto da release_date).
- Genres
Una lista dei generi usati per classificare l’album. Per esempio: “Prog Rock” , “Post-Grunge”. (Se non ancora classificato, l’array è vuoto).
Analisi sulle features
In questa sezione, cerchiamo di analizzare le varie features, il loro andamento, la loro correlazione, per verificare se emerge qualche pattern interessante.
getyear <- function(myString) {
as.integer(unlist(strsplit(myString, "-"))[1])
}
spotify_db$release_date <- sapply(spotify_db$release_date, as.character)
spotify_db <- mutate(spotify_db, year = sapply(spotify_db$release_date, getyear))
spotify_db <- mutate(spotify_db, duration_sec = duration_ms/1000)
ggplot(data = count(spotify_db, year)) +
geom_col(aes(x = year, y = n)) +
labs(title = "Numero di brani pubblicati nel tempo",
subtitle = "Statico dagli anni '40, in incremento dal 2000",
x ="Anno di pubblicazione",
y = "Numero di brani") +
theme_minimal()ggsave("./plots/tracksPubslished.png")Interessante notare come ci sia un costante aumento delle pubblicazioni annuali, con il picco di pubblicazioni nel 2020. Possiamo ipotizzare che questo picco sia legato all’anno della pandemia da Covid-19?
Procediamo ora con l’analisi della durata media (in secondi) dei brani negli anni, cercando anche di osservare se c’è un trend di crescita o decrescita.
mean_dur_by_year <- aggregate(spotify_db$duration_sec, list(spotify_db$year), mean)
colnames(mean_dur_by_year) <- c("Year","Avg_track_duration")
mean_dur_by_year %>%
ggplot(aes(x = Year, y = Avg_track_duration)) +
geom_point() +
geom_smooth() +
labs(title = "Durata media dei brani nel tempo",
subtitle = "In aumento fino agli anni '90 e poi in decrescita ",
x ="Anno in analisi",
y = "Durata media dei brani (in secondi)") +
theme_minimal()ggsave("./plots/trackDuration.png")Come si può notare, c’è un trend complessivo di aumento della durata, che però negli ultimi anni non è stato così marcato, anzi. Se ad esempio restringiamo il campo d’azione, analizzando i brani pubblicati dopo il 2005, potremmo quasi notare un trend inverso
filter(mean_dur_by_year, Year > 2005) %>%
ggplot(aes(x = Year, y = Avg_track_duration)) +
geom_point() +
geom_smooth() +
labs(title = "Durata media dei brani nel tempo",
subtitle = "Focus sull'inversione di tendenza iniziata dal 2011",
x ="Anno in analisi",
y = "Durata media dei brani (in secondi)") +
theme_minimal()ggsave("./plots/trackDurationFocus.png")Da questo focus si nota in maniera evidente il trend di decrescita, che sembra molto marcato.
Features dei brani
Ogni brano è caratterizzato dalle features descritte in introduzione. Può risultare interessante visualizzare con un boxplot la distribuzione di questi valori. È opportuno filtrare le features, per escludere quelle non legate alla natura musicale del brano.
spotify_db_filtered <- spotify_db %>%
select(-year,-explicit, -artists, -id, -name, -release_date, -duration_ms, -key, -loudness, -mode, -popularity, -tempo, -duration_sec, -id_artists, -time_signature)
spotify_db_filtered %>%
gather(key = "Feature", value = "Valore") %>%
ggplot(aes(x = reorder(Feature, Valore, FUN = median), y = Valore)) +
geom_boxplot(outlier.colour = "gray", outlier.alpha = 0.3, outlier.size = 0.3) +
labs(title = "Visualizazione dei valori assunti dalle varie features",
subtitle = "Analisi visiva mediante boxplot",
x = "Feature", y = "Valore") +
theme_minimal()ggsave("./plots/featuresBarPlot.png")Come si può notare dal grafico, instrumentalness, speechiness e liveness sono piuttosto tendenti a valori bassi, mentre le restanti features assumono sicuramente valori più alti, e sono anche distribuite molto più uniformemente. Confrontiamo ad esempio i grafici di densità di frequenza per acousticness ed energy.
spotify_db_features <- spotify_db_filtered %>%
gather(key = "Feature", value = "Valore")
spotify_db_features %>%
filter(Feature == "acousticness" | Feature == "energy") %>%
ggplot(aes(x = Valore)) +
geom_density(aes(fill = Feature), alpha = 0.4) +
labs(title = "Confronto distribuzione di acousticness ed energy",
subtitle = "Distribzione nettamente differente",
x = "Valore", y = "Densità") +
theme_minimal()ggsave("./plots/acousticnessEnergyDensity.png")Come si può notare, energy ha una distribuzione piuttosto uniforme, leggermente spostata a sinistra, mentre acousticness presenta un due picchi agli estremi. Osserviamo anche le distribuzioni delle altre features, escludendo instrumentalness che è praticamente formata da un unico picco in basso (come si può notare dal sottostante Summary)
summary(spotify_db$instrumentalness) Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000000 0.0000000 0.0000245 0.1134508 0.0095500 1.0000000
spotify_db_features %>%
filter(Feature == "speechiness") %>%
ggplot(aes(x = Valore)) +
geom_density(aes(fill = Feature), alpha = 0.4) +
labs(title = "Speechiness",
x = "Valore", y = "Densità") +
theme_minimal()spotify_db_features %>%
filter(Feature == "liveness") %>%
ggplot(aes(x = Valore)) +
geom_density(aes(fill = Feature), alpha = 0.4) +
labs(title = "Liveness",
x = "Valore", y = "Densità") +
theme_minimal()spotify_db_features %>%
filter(Feature == "valence") %>%
ggplot(aes(x = Valore)) +
geom_density(aes(fill = Feature), alpha = 0.4) +
labs(title = "Valence",
x = "Valore", y = "Densità") +
theme_minimal()spotify_db_features %>%
filter(Feature == "danceability") %>%
ggplot(aes(x = Valore)) +
geom_density(aes(fill = Feature), alpha = 0.4) +
labs(title = "Danceability",
x = "Valore", y = "Densità") +
theme_minimal()Correlazione tra le features
Abbiamo osservato qualche trend macroscopico, ma è interessante ora cercare di capire quanto le varie caratteristiche dei brani siano collegate. Per fare ciò viene utilizzato lo strumento statistico del coefficiente di correlazione di Pearson, che indica la dipendenza lineare che sussiste tra due variabili. Per visualizzare ciò è interessante utilizzare una Heatmap.
spotify_db_filtered <- spotify_db %>%
select(-year,-explicit, -artists, -id, -name, -release_date, -key, -mode, -id_artists, -duration_ms, -time_signature, -tempo)
corrMatrix <- round(cor(spotify_db_filtered, method = "pearson"), 3)
library(corrplot)
corrplot(corrMatrix, order = "hclust")Dall’analisi della correlazione, notiamo due interessanti legami
- Energy e Loudness, e in maniera minore Valence e Danceability sono direttamente correlate
- Energy e Acousticness, e in maniera minore Acousticness e Loudness sono inversamente correlate
Osserviamo graficamente questi fenomeni
spotify_db %>%
ggplot(aes(x=energy, y=loudness)) + geom_point() + geom_smooth(method = "lm") + labs(title = "Analisi di correlazione tra energia e volume dei brani", subtitle = paste("La linea di tendenza crescente testimonia la correlazione diretta, coefficiente: ", round(cor(spotify_db$energy, spotify_db$loudness, method = "pearson"), digits = 3)), x ="Energia", y = "Volume") + theme_minimal()spotify_db %>%
ggplot(aes(x=valence, y=danceability)) + geom_point() + geom_smooth(method = "lm") + labs(title = "Analisi di correlazione tra valenza e danzabilità e volume dei brani", subtitle = paste("Correlazione verificata, coefficiente: ", round(cor(spotify_db$valence, spotify_db$danceability, method = "pearson"), digits = 3)), x ="Valenza", y = "Danzabilità") + theme_minimal()spotify_db %>%
ggplot(aes(x=energy, y=acousticness)) + geom_point() + geom_smooth(method = "lm") + labs(title = "Analisi di correlazione tra energia e acusticità dei brani", subtitle = paste("La linea di tendenza decrescente testimonia la correlazione inversa, coefficiente: ", round(cor(spotify_db$energy, spotify_db$acousticness, method = "pearson"), digits = 3)), x ="Energia", y = "Acusticità") + theme_minimal()spotify_db %>%
ggplot(aes(x=acousticness, y=loudness)) + geom_point() + geom_smooth(method = "lm") + labs(title = "Analisi di correlazione tra acusticità e volume dei brani", subtitle = paste("Inversione meno marcata rispetto al caso precedente, coefficiente: ", round(cor(spotify_db$acousticness, spotify_db$loudness, method = "pearson"), digits = 3)), x = "Acusticità", y ="Volume") + theme_minimal()Queste due analisi grafiche confermano quanto ricavato dalla heatmap precedente.
Analisi musicale: toni e modi dominanti
Dal punto di vista musicale, è anche molto interessante effettuare un’analisi macroscopica delle tonalità e dei modi prevalentemente utilizzati nel brani. In breve, la tonalità indica l’accordo dominante nel brano e il modo indica se è maggiore (felice) o minore (triste). Nel dataset le tonalità sono indicate con numeri da 0 (Do) a 11 (Si). È opportuno tradurre questi numeri in note. Analogamente, il modo è indicato con 0 per minore e 1 per maggiore.
nomi_note <- c("Do","Do#","Re","Re#","Mi","Fa","Fa#","Sol","Sol#","La","La#","Si")
tracks_by_key <- spotify_db %>%
count(key, sort=TRUE) %>%
mutate(chiave = nomi_note[key+1]) %>%
select(chiave, n)
tracks_by_key %>%
ggplot(aes(x=reorder(chiave, -n), y=n)) + geom_bar(stat="identity") + labs(title = "Confronto tra le tonalità più utilizzate", subtitle = "Vincono quelle con meno alterazioni in chiave", x = "Tonalità", y = "Numero di brani") + theme_minimal()nomi_modi <- c("Minore","Maggiore")
tracks_by_mode <- spotify_db %>%
count(mode, sort=TRUE) %>%
mutate(modo = nomi_modi[mode+1]) %>%
select(modo, n)
ggplot(tracks_by_mode, aes(x = modo, y = n)) + geom_col() + labs(title = "Confronto tra i modi", subtitle = "Il modo maggiore è quello più utilizzato", x = "Modo", y = "Numero di brani") + theme_minimal()Come era lecito aspettarsi, le tonalità dominanti sono Do, Sol, Re, La e Fa, e il modo è quello Maggiore. Musicalmente, queste tonalità sono le più utilizzate per via di regole musicali per la scrittura dei brani, infatti sono le tonalità con meno alterazioni (diesis e bemolli), quindi più facili da scrivere e immediate da ascoltare. Inoltre, il modo maggiore risulta ampiamente più utilizzato del minore, proprio per la sua natura gioiosa.
Il dataset ci fornisce inoltre informazioni relative alla time signature dei brani, analizziamole.
spotify_db %>%
filter(time_signature > 1) %>%
count(time_signature, sort = TRUE) %>%
ggplot(aes(x = time_signature, y = n)) + geom_col() + labs(title = "Confronto tra time signature", subtitle = "Nettamente più utilizzato il classico 4/4, ma vanno fatte delle verifiche", x = "Time Signature", y = "Numero di brani") + theme_minimal() Ho rimosso i time signature 0/4 e 1/4, in quanto da musicista so che non esistono, ma sono dati da errori dell’algoritmo di rilevamento di Spotify (come spiegato, questa feature non è verificata ma solamente dedotta tramite algoritmo). La metrica 2/4 risulta a 0 per lo stesso motivo, probabilmente l’algoritmo la confonde con i 4/4. Allo stesso modo, è possibile affermare che molti altri tempi (specialmente composti) vengano “inglobati” dall’algoritmo in queste categorie. Questa feature insomma è la più “debole” probabilmente dell’intero dataset, e ci può quindi solamente fornire l’idea indicativa che vengono utilizzati tempi con metrica binaria o quaternaria, ma non ci da indicazioni sulla natura semplice o composta degli stessi.
Per concludere questo capitoletto, è interessante analizzare la popolarità dei brani.
most_popular_by_year = tibble(artists=NA,name=NA, year=NA, popularity=NA)
for (i in 1920:2021) {
dt <- spotify_db %>%
filter(year == i) %>%
select(artists, name, year, popularity) %>%
arrange(-popularity) %>%
head(1)
most_popular_by_year <- rbind(most_popular_by_year, dt)
}
most_popular_by_year %>%
ggplot(aes(x=year, y=popularity)) + geom_col() + labs(title = "Andamento dei brani più popolari nel tempo", subtitle = "Cresce nel tempo, tuttavia i brani dagli anni '60 restano molto popolari", x = "Anno", y = "Popolarità del brano più popolare") + geom_smooth() + theme_minimal()ggsave("./plots/popularity.png")Tramite questo grafico si può affermare che non conviene pubblicare brani ispirati alla musica relativa agli anni ’30, ’40 e ’50, in quanto non sono attualmente affatto popolari. Chiaramente i brani più recenti sono quelli attualmente più popolari, ma anche i brani degli anni ’60 - ’90 restano molto popolari.
Analisi dei generi
Iniziamo ora l’esplorazione dei generi musicali. Il dataset mette a disposizione un ulteriore file .csv, dove vengono riportate le informazioni relative ad artisti e generi. Vediamoli, elencati in base al numero di brani associati ad ogni genere.
spotify_genres <- read.csv("./data_by_genres.csv")
spotify_genres %>%
select(genres, popularity) %>%
arrange(-popularity) %>%
rename(
Genere = genres,
Popolarità = popularity
) %>%
head(10) Genere Popolarità
1 chinese electropop 79.00000
2 korean mask singer 78.00000
3 dutch rap pop 77.00000
4 yaoi 77.00000
5 dong-yo 76.00000
6 rochester mn indie 76.00000
7 afroswing 75.33333
8 estonian pop 75.00000
9 j-rap 75.00000
10 irish pop 74.62500
Risultati interessanti, visto che di questi generi, almeno la metà sono completamente sconosciuti per me. In ogni caso, dai nomi possiamo notare che si tratta principalmente di generi afferenti al pop, all’indie e al rap.
Analizziamo ora, in base ai generi, tonalità e modi.
topKey_by_genres <- spotify_genres %>%
count(key, sort=TRUE) %>%
mutate(chiave = nomi_note[key+1]) %>%
select(chiave, n)
topKey_by_genres %>%
ggplot(aes(x=reorder(chiave, -n), y=n)) + geom_bar(stat="identity") + labs(title = "Confronto tra le tonalità più utilizzate", subtitle = "Chiave di sol nettamente più utilizzata", x = "Tonalità", y = "Numero di brani") + theme_minimal()ggsave("./plots/topkeybygenres.png")topMode_by_genres <- spotify_genres %>%
count(mode, sort=TRUE) %>%
mutate(modo = nomi_modi[mode+1]) %>%
select(modo, n)
topMode_by_genres %>%
ggplot(aes(x = modo, y = n)) + geom_col() + labs(title = "Confronto tra i modi", subtitle = "Il modo maggiore rimane il più utilizzato", x = "Modo", y = "Numero di brani") + theme_minimal()ggsave("./plots/topmodebygenres.png")Per pura curiosità, siccome io sono appassionato di modo minore, verifichiamo quali sono i generi più popolari realizzati in modo minore.
spotify_genres %>%
filter(mode == 0) %>%
arrange(-popularity) %>%
select(genres, popularity) %>%
head(10) genres popularity
1 chinese electropop 79.00000
2 korean mask singer 78.00000
3 yaoi 77.00000
4 afroswing 75.33333
5 j-rap 75.00000
6 alberta hip hop 74.50000
7 channel islands indie 74.00000
8 instrumental grime 73.13333
9 london rap 72.33333
10 japanese teen pop 71.66667
Analisi dei trend musicali nel tempo
Dopo aver osservato in maniera descrittiva le singole features, può risultare interessante andare ad osservare la loro evoluzione temporale. In primo luogo è sicuramente interessante osservare l’andamento medio delle features nel tempo.
features_by_year <- spotify_db %>%
select(-artists, -explicit, -id, -key, -mode, -name, -release_date, -duration_ms, -tempo, -loudness, -popularity, -duration_sec, -id_artists) %>%
filter(year > 1900) %>%
group_by(year) %>%
summarise(across(danceability:valence, mean, na.rm = TRUE, .names = "{col}_mean")) %>%
pivot_longer(
cols = danceability_mean:valence_mean,
names_to = "feature_full",
values_to = "Value"
) %>%
separate(feature_full, into = c("Feature","applied_function"), sep = "_")
colors <- c(acousticness_mean = "red",
danceability_mean = "green",
energy_mean = "blue",
instrumentalness_mean = "yellow",
liveness_mean = "pink",
speechiness_mean = "orange",
valence_mean = "gray"
)
features_by_year %>%
ggplot(aes(x=year, y=Value, group=Feature, color = Feature)) +
geom_line() +
scale_color_discrete(name = "Legenda") +
labs(title = "Andamento delle features nel tempo", subtitle = "Interessante andamento di Acousticness ed Energy", x = "Anno", y = "Valore") + theme_minimal()ggsave("./plots/featuresovertime.png")Fino agli anni ’50 c’è un andamento irregolare di tutti i dati, probabilmente dato dall’incompletezza del dataset per dati così antiquati. Interessante notare l’andamento di Energy e Acousticness: il primo aumenta nettamente nel tempo, mentre il secondo al contrario ha un brusco calo. Isoliamoli ed analizziamoli meglio.
features_by_year %>%
filter(year >= 1950 & (Feature == "acousticness" | Feature == "energy")) %>%
ggplot(aes(x=year, y=Value, group=Feature, color = Feature)) +
geom_line() +
geom_smooth() +
scale_color_discrete(name = "Legenda") +
labs(title = "Focus su Acousticness ed Energy", subtitle = "Interessante andamento di Acousticness ed Energy", x = "Anno", y = "Valore") + theme_minimal()Come si può notare, le due features hanno un valore medio molto similare poco prima del 1970. Per il resto, globalmente il trend resta decrescente per Acousticness e crescente per Energy, seppure i valori sembrino recentemente mediamente piatti. Tutto ciò è un’ulteriore conferma di quanto espresso in precedenza su queste due features, tramite correlazione di Pearson.
Potenziali sviluppi futuri
Effettuare questa analisi ci ha permesso di comprendere meglio l’attuale panorama musicale presente su Spotify, e già con queste informazioni, un nostro ipotetico musicista in erba avrebbe molte informazioni su cui meditare, relativamente al modo in cui strutturare le sue uscite musicali. Ora però sarebbe interessante spingersi anche un po’ oltre e provare, sulla base dei dati raccolti, ad effettuare delle previsioni. Per fare ciò, ho scelto di impostare ed utilizzare un modello di regressione polinomiale, applicandolo alle varie features.
modelAcousticness <- lm(acousticness ~ poly(year, 2, raw = TRUE), data = spotify_db)
p1 <- predict(modelAcousticness, data.frame(year=c(2022:2027)))
modelEnergy <- lm(energy ~ poly(year, 2, raw = TRUE), data = spotify_db)
p2 <- predict(modelEnergy, data.frame(year=c(2022:2027)))
modelDanceability <- lm(danceability ~ poly(year, 2, raw = TRUE), data = spotify_db)
p3 <- predict(modelDanceability, data.frame(year=c(2022:2027)))
modelInstrumentalness <- lm(instrumentalness ~ poly(year, 2, raw = TRUE), data = spotify_db)
p4 <- predict(modelInstrumentalness, data.frame(year=c(2022:2027)))
modelLiveness <- lm(liveness ~ poly(year, 2, raw = TRUE), data = spotify_db)
p5 <- predict(modelLiveness, data.frame(year=c(2022:2027)))
modelSpeechiness <- lm(speechiness ~ poly(year, 2, raw = TRUE), data = spotify_db)
p6 <- predict(modelSpeechiness, data.frame(year=c(2022:2027)))
modelValence <- lm(valence ~ poly(year, 2, raw = TRUE), data = spotify_db)
p7 <- predict(modelValence, data.frame(year=c(2022:2027)))
Valori <- format(c(p1,p2,p3,p4,p5,p6,p7), scientific = FALSE)
Anno <- c(rep(c(2022,2023,2024,2025,2026,2027),7))
Feature <- c(rep("Acousticness",6),rep("Energy",6),rep("Danceability",6),rep("Instrumentalness",6),rep("Liveness",6),rep("Speechiness",6),rep("Valence",6))
test <- data.frame(Feature, Anno, Valori)
test$Valori <- sapply(test$Valori, as.numeric)
test$Valori = test$Valori/100
test %>%
filter(Anno == 2022) %>%
select(Feature, Valori) Feature Valori
1 Acousticness 0.24
2 Energy 0.37
3 Danceability 0.31
4 Instrumentalness 0.01
5 Liveness 0.18
6 Speechiness 0.05
7 Valence 0.30
ggplot(test, aes(x = Anno, y = Valori, color = Feature)) +
geom_line() +
geom_point() +
labs(title = "Previsione quinquennale", subtitle = "Valori delle features", x = "Anno", y = "Valore previsto") + theme_minimal()ggsave("./plots/futurefeatures.png")Da questa analisi, possiamo vedere quali sono i valori medi previsti per le varie features per il prossimo anno, e anche il loro andameno quinquennale previsto. Indicativamente, si prospettano brani piuttosto energici e ballabili, poco strumentali e acustici, non registrati in live, con un mood lievemente tendente verso il triste o greve.
Sentiment Analysis
In conclusione, può risultare molto interessante analizzare i testi dei brani più popolari, per osservare l’evoluzione (se c’è) dei sentimenti dei testi più popolari nel tempo.
L’idea è di costruire un dataframe apposito, con i due brani più popolari di ogni anno. Per ogni riga, quindi per ogni brano, è necessario ottenere il testo, su cui in seguito effettuare la sentiment analysis.
sentimentAnalysisDF = tibble(artists=NA,name=NA, year=NA, popularity=NA)
for (i in 1960:2021) {
dt <- spotify_db %>%
filter(year == i) %>%
select(artists, name, year, popularity) %>%
arrange(-popularity) %>%
head(1)
sentimentAnalysisDF <- rbind(sentimentAnalysisDF, dt)
}
sentimentAnalysisDF$artists <- sapply(sentimentAnalysisDF$artists, as.character)
sentimentAnalysisDF$artists <- str_sub(sentimentAnalysisDF$artists, 2, -2)
sentimentAnalysisDF <- drop_na(sentimentAnalysisDF)
sentimentAnalysisDF = sentimentAnalysisDF %>%
separate(artists, "Artista", sep = ",")
sentimentAnalysisDF$Artista <- str_sub(sentimentAnalysisDF$Artista, 2, -2)
sentimentAnalysisDF[, "text"] <- ""
sentimentAnalysisDF = sentimentAnalysisDF %>%
mutate(queryAdd = paste("http://api.chartlyrics.com/apiv1.asmx/SearchLyricDirect?artist=", Artista, "&song=", name, sep = "")) %>%
mutate(queryAdd = str_replace_all(queryAdd, " ", "%20"))
library(XML)
for (song in 1:124) {
tryCatch ( {
xmlRawData = xmlToList(sentimentAnalysisDF$queryAdd[song], addAttributes = TRUE, simplify = FALSE)
testo = xmlRawData$Lyric
sentimentAnalysisDF$text[song] = testo
},
error = function(err) {
cat("Errore, testo mancante")
}
)
}failed to load HTTP resource
Errore, testo mancantefailed to load HTTP resource
Errore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancanteErrore, testo mancante
Utilizzando la API ChartLyrics è stato possibile ottenere molti testi in maniera automatizzata. Purtroppo, essendo una soluzione gratuita non include tutti i testi. I mancanti li inserisco a mano.
library(tidytext)
sentimentFinalTable <- sentimentAnalysisDF %>%
mutate(songId = row_number()) %>%
ungroup() %>%
unnest_tokens(word, text) %>%
inner_join(get_sentiments("bing")) %>%
count(name, anno = year, sentiment) %>%
spread(sentiment, n, fill = 0) %>%
mutate(sentiment = positive - negative) %>%
arrange(anno)
summary(sentimentFinalTable$sentiment) Min. 1st Qu. Median Mean 3rd Qu. Max.
-32.000 -4.000 0.500 1.758 8.000 36.000
sentimentFinalTable %>%
ggplot(aes(anno,sentiment, fill=name)) +
geom_col(show.legend = FALSE) +
labs(title = "Andamento del Sentiment", subtitle = "Canzone più popolare per ciascun anno", x = "Anno", y = "Valore di sentiment") + theme_minimal()ggsave("./plots/sentiment.png")Questa analisi purtroppo non ha restituito nessun pattern interessante; sentimenti positivi e negativi si alternano senza trend o valori predominanti. L’unica cosa che si può ricavare è che appunto, il sentiment del testo dei brani non ha una grande rilevanza ai fini della popolarità.
Conclusioni
Al termine di questo progetto, è possibile ricavare alcune interessanti conclusioni che possono aiutarci a rispondere alle domande in oggetto: quali sono i parametri musicali maggiormente utilizzati su Spotify? In che modo un musicista emergente può essere relativamente sicuro di pubblicare brani che verranno apprezzati dalla massa?
Innanzitutto, è doveroso ricordare che siamo nel campo delle ipotesi: questa analisi ci può soltanto aiutare, ci fa avere una panoramica di tutto ciò. Ma stiamo parlando di musica, emozioni umane, e soprattutto di un’analisi non professionale, basata su dati circoscritti territorialmente.
Detto ciò, traiamo le conclusioni:
La durata media dei brani si attesta intorno ai 200 secondi, quindi circa 3 minuti e 20 secondi, con un trend di decrescita: è opportuno quindi pensare di pubblicare brani leggermente più corti
Strumentalità, parlato e liveness si attestano verso valori bassi, in trend decrescente: è opportuno quindi pensare di pubblicare brani non prevalentemente strumentali o parlati, e non registrati in live
Energia, valenza e danzabilità si attestano su valori medio/alti, in trend crescente/stabile: via libera a brani energici, positivi e movimentati
Le tonalità più utilizzate sono quelle con meno alterazioni in chiave, quindi è opportuno pensare di utilizzare il classico circolo delle quinte (do - sol - re - la - fa)
Le misure più utilizzate sono quelle a multipli di due (2/4, 4/4), quindi meglio evitare misure ternarie o dispari
La popolarità dei brani antecedenti agli anni ’60 risulta in discesa, quindi meglio ispirarsi solo a brani pubblicati successivamente
In merito al testo non sono stati trovati pattern da seguire, quindi libero sfogo ad emozioni sia positive che negative